﻿using System;
using System.Collections.Generic;
using System.Configuration;
using Less;
using Less.Core.Loggers;
using Less.Core;
using Less.Core.Model;
using Less.Tools;

/// <summary>
/// Helper class to trace the application behavior
/// </summary>
public static class Log
{
  
    #region LogPresets enum
    /// <summary>
    /// used to categorize an entry. The greater the importance
    /// </summary>
    public enum LogPresets
    {
        None,
        Debug,
        Beta,
        Production
    }
    #endregion
    private static List<ILogger> _Loggers;

    private static LogPresets? _LogPreset;
    public static LogPresets LogPreset
    {
        get
        {
            if ( !_LogPreset.HasValue )
            {
                LogPresets value = LogPresets.None;
                String name = ConfigurationManager.AppSettings["Log.LogPreset"];
                if (name != null) Enum.TryParse(name, out value);
                _LogPreset= value;
            }
            return _LogPreset.GetValueOrDefault(LogPresets.None);
        }
        set { _LogPreset = value; }
    }

    private static EntrySeverities? _MinSeverity;
    public static EntrySeverities MinSeverity
    {
        get
        {
            if ( !_MinSeverity.HasValue )
            {
                EntrySeverities value = EntrySeverities.Debug;
                String name = ConfigurationManager.AppSettings["Log.MinSeverity"];
                if ( name != null ) Enum.TryParse(name, out value);
                _MinSeverity = value;
            }
            return _MinSeverity.GetValueOrDefault(EntrySeverities.Debug);
        }
        set { _MinSeverity = value; }
    }

    private static bool? _EnableMetrics;
    public static bool EnableMetrics
    {
        get
        {
            if ( !_EnableMetrics.HasValue )
            {
                // ToDo: Test web config 
                bool value = true;
                string name = ConfigurationManager.AppSettings["Log.EnableMetrics"];
                if (name != null) value = Boolean.TryParse(name, out value);
                _EnableMetrics = value;
            }
            return _EnableMetrics.GetValueOrDefault(true);
        }
        set { _EnableMetrics = value; }
    }


    /// <summary>
    /// List of all registered loggers
    /// </summary>
    public static List<ILogger> Loggers
    {
        get
        {
            if (_Loggers.IsNullOrEmpty())
            {
                switch (LogPreset)
                {
                    case LogPresets.None:
                        _Loggers = new List<ILogger> { new TraceLogger()};
                        break;
                    case LogPresets.Debug:
                        _Loggers = new List<ILogger> { new TraceLogger(), new FileLogger() };
                        break;
                    case LogPresets.Beta:
                        _Loggers = new List<ILogger> { new FileLogger(), new MailLogger() };
                        break;
                    case LogPresets.Production:
                        _Loggers = new List<ILogger> { new FileLogger(), new MailLogger() };
                        break;
                    default:
                        _Loggers = new List<ILogger> { new TraceLogger() };
                        break;
                }
            }
            return _Loggers;
        }
        set { _Loggers = value; }
    }

    /// <summary>
    /// Main writer method, will call each logger based on the entry Severity
    /// </summary>
    private static void Write(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory = EntryCategories.Base,
                              string entrySource = "", string userName = "",
                              long processId = 0, string metricName = "", object metricValue = null
        )
    {
        entryMessage = Functions.ShortString(Functions.CleanMessage(entryMessage));
        if ((int)entrySeverity >= (int)LogPreset && entrySeverity>=MinSeverity)
        {
            foreach (ILogger logger in Loggers)
            {
                if (entrySeverity >= logger.MinSeverity)
                {
                    try
                    {
                        logger.Write(entrySeverity, entryMessage, entryCategory, entrySource, userName, processId, metricName, metricValue);
                    }
                    catch (Exception e)
                    {
                        Status.AddMessage(new LogEntry() { Message = e.ToLogString(), Category = EntryCategories.Log, Severity = EntrySeverities.Fatal });
                    }
                }
            }
        }
    }

    /// <summary>
    /// Writes message with the lowest severity. Normally used at development time
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    public static void Debug(string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string userName = "")
    {
        Write(EntrySeverities.Debug, entryMessage, entryCategory, "", userName);
    }

    /// <summary>
    /// Writes message with low severity. Normally used at beta testing time
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    public static void Info(string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string userName = "")
    {
        Write(EntrySeverities.Info, entryMessage, entryCategory, "", userName);
    }

    /// <summary>
    /// Writes performance related messages. Normally used to trace the behaviour of the application
    /// </summary>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    /// <param name="processName"></param>
    public static long ProcessStart(EntryCategories entryCategory = EntryCategories.Base, string userName = "", string processName = "")
    {
        long processId = DateTime.Now.Ticks;
        if (EnableMetrics)
        {
            var entrySource = Functions.GetCallingMethodName();
            Write(EntrySeverities.Metric, "START: " + processName, entryCategory, entrySource, userName, processId);
        }
        return processId;

    }

    /// <summary>
    /// Writes performance related messages. Normally used to trace the behaviour of the application
    /// </summary>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    /// <param name="processName"></param>
    /// <param name="processId"></param>
    /// <param name="metricName"></param>
    /// <param name="metricValue"></param>
    public static void ProcessEnd(EntryCategories entryCategory = EntryCategories.Base, string userName = "", string processName = "", long processId = 0, string metricName = "", object metricValue = null)
    {
        if (EnableMetrics)
        {
            var entrySource = Functions.GetCallingMethodName();
            Write(EntrySeverities.Metric, "END: " + processName, entryCategory, entrySource, userName, processId, metricName, metricValue);
        }
    }

    public static void Metric(EntryCategories entryCategory = EntryCategories.Base, string userName = "", string metricName = "", object metricValue = null)
    {
        if (EnableMetrics)
        {
            var entrySource = Functions.GetCallingMethodName();
            Write(EntrySeverities.Metric, "", entryCategory, entrySource, userName, 0, metricName, metricValue);
        }
    }


    /// <summary>
    /// Writes a warning message. Is the lowest error messages. Normally indicates a condition not satisfeid, but controlled.
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    public static void Warning(string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string userName = "")
    {
        Write(EntrySeverities.Warning, entryMessage, entryCategory);
    }

    /// <summary>
    /// Writes an error message. Normally indicates an exception expected caught, and allows the program to continue
    /// </summary>
    /// <param name="exception">The exception that cases the entry</param>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    /// <param name="entrySource"></param>
    public static void Error(Exception exception, EntryCategories entryCategory = EntryCategories.Base, string userName = "", string entrySource = "")
    {
        if (entrySource.IsNullOrEmpty())
        {
            entrySource = Functions.GetCallingMethodName();
        }
        Write(EntrySeverities.Error, exception.ToLogString(), entryCategory, entrySource);
    }

    /// <summary>
    /// Highest severity. Exception unexpected, uncaught or one thats forces the program to restart.
    /// </summary>
    /// <param name="exception">The exception that cases the entry</param>
    /// <param name="entryCategory"></param>
    /// <param name="userName"></param>
    /// <param name="entrySource"></param>
    public static void Fatal(Exception exception, EntryCategories entryCategory = EntryCategories.Base, string userName = "", string entrySource = "")
    {
        if (entrySource.IsNullOrEmpty())
        {
            entrySource = Functions.GetCallingMethodName();
        }
        Write(EntrySeverities.Fatal, exception.ToLogString(), entryCategory, entrySource);
    }
    #region Nested type: ILogger
    /// <summary>
    /// The basic behaviour of any logger
    /// </summary>
    public interface ILogger
    {
        EntrySeverities MinSeverity { get; set; }
        bool Write(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory = EntryCategories.Base, string entrySource = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null);
        bool WriteMessage(string message);
        string ComposeMessage(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory, string entrySource = "", string userName = "", long processId = 0, string metricName = "", object metricValue = null);
        string FormatTimeStamp(DateTime timeStamp);
        string FormatSeverity(EntrySeverities logSeverity);
        string FormatCategory(EntryCategories logCategory);
    }
    #endregion


}



